import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plot
import matplotlib.animation as animation
import numpy
import Tkinter
from Tkinter import *
import serial
from searchGUI import searchGUI
from saveGUI import saveGUI
from dialogBox import dialogBox
from pygame import mixer
from Queue import Queue
import threading
import time

class ecgGUI(Tkinter.Tk):
    
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        
        self.withdraw()
        
        self.parent = parent
        self.setup_data()
        self.initialize()
        self.setup_serial()
        
        self.deiconify()
    
    def setup_serial(self):
        ser = serial.Serial()
        ser.baudrate = 57600
        ser.port = 'COM5'
        
        try:
            ser.open()
        except:
            message = "Could not connect to device"
            self.dialogApp = dialogBox(self,message)
            self.dialogApp.title('ECG System')          
            self.wait_window(self.dialogApp)
            
        self.ser = ser
        self.buff_thread = threading.Thread(target=self.bufferData, args=())
        self.buff_thread.start()
        self.data_gen_thread = threading.Thread(target=self.data_gen,args=())
        self.data_gen_thread.start()
        self.canvas.get_tk_widget().after(10, self.update_canvas)
        self.start = time.clock() - 0.01
        #render_thread = threading.Thread(target=self.update_canvas, args=())
        #render_thread.start()
    
    def setup_data(self):
        self.file1 = open("ecg_test_hard.txt","r")
        
        self.size = 10000
        self.maximum = 512.0
        
        self.rawPoints = []
        self.finalPoints = []
        
        for i in range(0,self.size):
            self.rawPoints.append(0)
            self.finalPoints.append(0)
            
        mixer.init() #Initialize mixer
        self.alert = mixer.Sound('coin.wav') 
        self.thresh = .5 #Initialize threshold
        self.factor = 1024.0
        self.q = Queue(10000)
        self.delay = 0.0095
        
        
    def initialize(self):
        frame1 = Frame()      
        
        self.entryVariable = StringVar()
        self.entryVariable.set("Enter search term here.")
        self.entry = Entry(frame1, textvariable=self.entryVariable)
        self.entry.bind("<Return>", self.OnSearch)
        self.entry.bind("<FocusIn>", self.OnFocus) 
        #self.entry.pack(side='right', anchor='e')
        
        searchLabel = Label(frame1, text="Search Data:")
        #searchLabel.pack(side='right', anchor='e')           
        
        frame1.pack(fill='both', padx=(10,10), pady=(10,0))
        
        frame2 = Frame()
        
        advancedLabel = Label(frame2, text="Advanced Search", anchor='e')
        advancedLabel.bind("<Button-1>", self.OnAdvanced)
        advancedLabel.pack(side='right', anchor='e')
        
        frame2.pack(fill='both', padx=(10,10))
        
        frame3 = Frame()
        
        saveButton = Button(frame3, text="Save Snapshot", command=self.OnSave)
        saveButton.pack(side='top', anchor='w')        
        
        figure = Figure()
        self.axis = figure.add_subplot(111)
        self.axis.set_ylim(-1,1)
        self.axis.set_xlabel("elapsed time (ms)")
        self.axis.set_ylabel("voltage (V)")
        self.line, = self.axis.plot(self.finalPoints, animated=True)
        self.canvas = FigureCanvasTkAgg(figure, master=frame3)
        self.canvas.mpl_connect('draw_event', self.update_background)
        self.canvas.get_tk_widget().pack(side='bottom', expand=True, fill='both')
        self.canvas.draw()

        label = Label(frame3, text="Heart rate: ")
        label.pack(anchor='e')

        self.heartVar = StringVar()
        self.heartVar.set("0")
        heartLabel = Label(frame3, textvariable=self.heartVar)
        heartLabel.pack(anchor='e')
        
        frame3.pack(fill='both', padx=(10,10))
        
        frame4 = Frame()
        
        settingsLabel = Label(frame4, text="Settings")
        settingsLabel.pack(side='top', anchor='w')
        
        frame4.pack(fill='both', padx=(10,10), pady=(5,10))
        
        frame5 = Frame()

        gainLabel = Label(frame5, text="Select gain: ")
        gainLabel.pack(side='left', anchor='w', padx=(0,10)) 
        
        options = ("Low", "High")
        self.gainVar = StringVar()
        self.gainVar.trace('w', self.OnGainSettings)
        self.gainVar.set(options[0])
        gainMenu = OptionMenu(frame5, self.gainVar, *options)
        gainMenu.pack(side='left', anchor='w', padx=(10,20)  )     
        
        notchLabel = Label(frame5, text="Notch filter: ")
        notchLabel.pack(side='left', anchor='w', padx=(20,10))
        
        options = ("On", "Off")
        self.notchVar = StringVar()
        self.notchVar.set(options[1])
        notchMenu = OptionMenu(frame5, self.notchVar, *options)
        notchMenu.pack(side='left', anchor='w', padx=(10,0))
        
        frame5.pack(fill='both', padx=(10,10), pady=(0,10))
        
        self.resizable(True, True)
        self.update()
        
        self.protocol("WM_DELETE_WINDOW", self.OnClose)
    
    def update_background(self,event):
        self.background = self.canvas.copy_from_bbox(self.axis.bbox)
        
    def update_canvas(self, *args):
        #self.data_gen()

        self.canvas.restore_region(self.background)
        self.line.set_ydata(self.finalPoints)
        self.axis.draw_artist(self.line)      
        self.canvas.blit(self.axis.bbox)
    
        self.canvas.get_tk_widget().after(10, self.update_canvas)
    
    def data_gen(self, *args):
#        if(self.ser.isOpen() == False):
#            message = "Could not connect to device"
#            self.dialogApp = dialogBox(self,message)
#            self.dialogApp.title('ECG System')          
#            self.wait_window(self.dialogApp)
        while True:
            for i in range(0,10):
                if(not self.ser.isOpen()):
                    number = float(self.file1.readline())/self.maximum
                else:
                    number = self.q.get()
                    self.q.task_done()
                while(len(self.rawPoints) >= self.size):
                    self.rawPoints.pop(len(self.rawPoints)-1)
                self.rawPoints.insert(0,number)

            #if(self.notchVar.get() == "On"):
            #    points = self.applyNotch(self.rawPoints)
            #    qrsPoints = points[:10]
            #else:
            #    qrsPoints = self.rawPoints[:10]

            
            qrsPoints = self.rawPoints[:10]
            self.detect_qrs(qrsPoints)
            self.finalPoints = self.rawPoints
            #self.finalPoints = self.rawPoints
            if(not self.ser.isOpen()):
                time.sleep(self.delay)
    
    def bufferData(self):
        while True:
            thing0 = self.ser.read(1)
            thing1 = self.ser.read(1)
            thing2 = self.ser.read(1)
            if thing0 == 'a' and thing1 == 'b' and thing2 == 'c':
                data = self.ser.read(2)
                data = int(data.encode('hex'), 16)
                if(data < 1024 and data > -1):
                    self.q.put(data / self.factor)
                    #print data
        
    def detect_qrs(self,points):
        maxVal = -1
        place = 50;
        for i in range(0,10):
            if(points[i] > maxVal):
                maxVal = points[i]
                place = i
        if(maxVal > self.thresh and place > 0):
            self.end = time.clock()
            elapsed = self.end - self.start
            if(elapsed > 0):
                heartRate = int((1.0/(elapsed))*60.0)
                self.start = time.clock()
                self.thresh = maxVal
            if(heartRate > 40 and heartRate < 120):
                self.alert.play()
                self.heartVar.set(heartRate)
            else:
                self.heartVar.set("NA")
        else:
            self.thresh = self.thresh - .002
        #print self.thresh;

    def detect_qrs_fixed(self,points):
        maxVal = -1
        place = 50;
        for i in range(0,10):
            if(points[i] > maxVal):
                maxVal = points[i]
                place = i
        if(maxVal > self.thresh and place > 0):
            self.thresh = maxVal
            self.alert.play()
        else:
            self.thresh = self.thresh - .002

    def applyNotch(self,points):
        ts = 1.0/1000.0
        bandwidth = 100
        a1 = 2*numpy.cos(2*numpy.pi*60*ts)/(1+numpy.tan(bandwidth*ts/2))
        a2 = (1-numpy.tan(bandwidth*ts/2))/(1+numpy.tan(bandwidth*ts/2))
        time = numpy.linspace(0, 10, num=1000)
        data = numpy.sin(2*numpy.pi*60*time)
                
        qm1 = 0
        qm2 = 0
        result = []
        result.append(points[0])
        for k in range(1,len(points)):
            i = points[k]
            ww = i + qm2
            zz = a1*qm1-a2*ww
            q = i + zz
            o = ww - zz
            result.append(o)
            qm2 = qm1
            qm1 = q
        return result  
    
    def OnSave(self):
        self.saveApp = saveGUI(self,self.finalPoints)
        self.saveApp.title('Save Snapshot')   
        
    def OnSearch(self, event):
        self.entry.focus_set()
        self.entry.selection_range(0, Tkinter.END)
        
        if(self.entryVariable.get() != ""):
            self.searchApp = searchGUI(self,self.entryVariable.get())
            self.searchApp.title("Search Data")
            self.searchApp.mainloop()
    
    def OnAdvanced(self, event):
        self.searchApp = searchGUI(self,"")
        self.searchApp.title("Search Data")
        self.searchApp.mainloop()
                
    def OnNotchSettings(self,varName,index,mode):
        if self.notchVar.get() == "On":
            print "turn on"
        else:
            print "turn off"
        
    def OnGainSettings(self,varName,index,mode):
        if(self.gainVar.get() == "High"):
            self.factor = 102.4
            if(self.ser.isOpen()):
                self.ser.write('H')
            self.axis.set_ylim(-10, 10)
            self.axis.draw_artist(self.axis)
            self.canvas.draw()
        else:
            self.factor = 1024.0
            self.thresh = 0.5
            if(self.ser.isOpen()):
                self.ser.write('L')
            self.axis.set_ylim(-1, 1)
            self.axis.draw_artist(self.axis)
            self.canvas.draw()
        
    def OnClose(self):
        if(self.ser.isOpen() == True):
            self.ser.flushInput()
            self.ser.close()
        self.destroy()

    
    def OnFocus(self,event):
        event.focus_set()
        event.selection_range(0, Tkinter.END)    

if __name__ == "__main__":
    app = ecgGUI(None)
    app.title('ECG System')
    app.mainloop()
